package com.apobates.jforum.grief.schema2doc;

import com.apobates.jforum.grief.schema2doc.output.freeMarker.ExportTemplate;
import com.apobates.jforum.grief.schema2doc.core.entity.Table;
import com.apobates.jforum.grief.schema2doc.jdbc.ExportExecutor;
import com.apobates.jforum.grief.schema2doc.output.docx.DocxWordOutput;
import com.apobates.jforum.grief.schema2doc.core.schema.SchemaDialect;
import com.apobates.jforum.grief.schema2doc.core.strategy.SchemaTableContainsStrategy;
import com.apobates.jforum.grief.schema2doc.core.strategy.SchemaTablePrefixStrategy;
import com.apobates.jforum.grief.schema2doc.jdbc.out.ConsoleSchemaOutput;
import com.apobates.jforum.grief.schema2doc.jdbc.schema.ResultSupplier;
import com.apobates.jforum.grief.schema2doc.core.entity.Information;
import com.apobates.jforum.grief.schema2doc.core.strategy.SchemaTableRegMatchStrategy;
import com.apobates.jforum.grief.schema2doc.jdbc.dialect.JooqQueryMySQLExecutor;
import com.apobates.jforum.grief.schema2doc.output.freeMarker.FreeMarkerWordOutput;
import com.apobates.jforum.grief.schema2doc.reactive.*;
import com.apobates.jforum.grief.schema2doc.reactive.dialect.RxJavaPostgreSQLExecutor;
import com.apobates.jforum.grief.schema2doc.reactive.out.ConsoleSchemaObservableOutput;
import com.apobates.jforum.grief.schema2doc.reactive.out.DocxWordObservableOutput;
import com.apobates.jforum.grief.schema2doc.reactive.out.DocxWordReactiveOutput;
import com.apobates.jforum.grief.schema2doc.reactive.schema.ResultRxJavaSupplier;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import io.reactivex.disposables.Disposable;
import org.davidmoten.rx.jdbc.Database;
import org.davidmoten.rx.jdbc.pool.DatabaseType;

import javax.sql.DataSource;
import java.util.concurrent.TimeUnit;

/**
 * 使用: Screw生成表结构的doc文档
 * @see ://github.com/pingfangushi/screw
 */
public class App {
    // screw 配置
    private static final String FILE_OUTPUT_DIR = "d:/schema";
    private static final String DOC_FILE_NAME = "管理系统的数据库设计文档";// 自定义文件名称 即数据库文档名称
    // private static final EngineFileType FILE_OUTPUT_TYPE = EngineFileType.WORD; // 文件类型 HTML->HTML文件  WORD->WORD文件  MD->Markdown文件
    private static final String DOC_VERSION = "1.0.0";
    private static final String DOC_DESCRIPTION = "管理系统的数据库设计文档";
    // DB 配置
    // MySQL驱动 6.0以前的使用com.mysql.jdbc.Driver，6.0以后的使用com.mysql.cj.jdbc.Driver  我这边使用的是8.0.20版本的
    private static final String Driver_Class_Name = "com.mysql.cj.jdbc.Driver";
    // 数据库种类 MySQL  Oracle  SQLServer
    // private static final String DB_TYPE = "MySQL";
    private static final String DB_URL = "jdbc:mysql://bj-cdb-7ub9yi6g.sql.tencentcdb.com:60264";
    private static final String DB_USERNAME = "crm_system";
    private static final String DB_PASSWORD = "JJXZ@530";
    // MySQL数据库名、Oracle实例名、SQLServer版本名和数据库名
    private static final String DB_NAME = "discuz";// MySQL
    // private static final String IGNORE_MISTAKEN_CODE  = "?characterEncoding=UTF-8";//  防止生成后文档乱码
    private static final RxJavaCompleteHandler completeHandler = ()->System.exit(0);
    public static void main(String[] args) {
        // 基于模板的自定义
        final Information info = new Information(DOC_FILE_NAME, DB_NAME, DOC_VERSION, DOC_DESCRIPTION);
        /* 环境中有DataSource */
        // 环境中无DataSource
        // postgresqlExport(info);
        mysqlExport(info);
        // oracleExport(info);
        // mssqlExport(info);
        // sqliteExport(info);
        // informixExport(info);
        // ---------------------------------->
        // rxMySQL(info);
        // postgreSQL(info);
        // querySchema();
    }
    // MySql
    private static void mysqlExport(Information info){
        // 环境中有DataSource
        // .strategy(new SchemaTableRegMatchStrategy("'^(qrtz|spring)_'")) // 导出表的筛选策略|不设置相当于导出所有|new SchemaTablePrefixStrategy("apo")
        /*
        try {
            ExportExecutor
                    .supply(new JooqQueryMySQLExecutor(configHikariDataSource(), SchemaDialect.MySQL)) // 导出结果集的供应商|根据配置决定用哪个?
                    .target(new DocxWordOutput(FILE_OUTPUT_DIR)) // 导出目标|模板咱选择?basic?full?
                    .template(ExportTemplate.full()) // 自定义模板的实现
                    .database("ftpro") // 导出的数据库名称
                    .export(info); // 执行导出
        }catch (Exception e){
            e.printStackTrace();
        }*/
        try {
            ExportExecutor
                    .supply(ResultSupplier
                            .mysql()
                            .url(DB_URL)
                            .database(DB_NAME)
                            .account(DB_USERNAME,DB_PASSWORD)
                            .executor()) // 导出结果集的供应商|根据配置决定用哪个?
                    .target(new DocxWordOutput(FILE_OUTPUT_DIR)) // 导出目标|模板咱选择?basic?full?
                    .template(ExportTemplate.full()) // 自定义模板的实现
                    .database(DB_NAME) // 导出的数据库名称
                    .export(info); // 执行导出
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    // SQLite 3
    private static void sqliteExport(Information info){
        String database="discuz";
        ExportExecutor
                .supply(ResultSupplier
                        .sqlite()
                        .url("jdbc:sqlite:D:/sqlite/" + database + ".db")
                        .database(database)
                        .anonymous()
                        .executor()
                )
                .strategy(new SchemaTablePrefixStrategy("sys")) // 导出表的筛选策略|不设置相当于导出所有
                .target(new ConsoleSchemaOutput()) // 导出目标|模板咱选择?basic?full?
                .template(ExportTemplate.full()) // 自定义模板的实现
                .database(database) // 导出的数据库名称
                .export(info); // 执行导出
    }
    // Informix
    private static void informixExport(Information info){
        ExportExecutor
                .supply(ResultSupplier
                        .informix()
                        .url("jdbc:informix-sqli://localhost:9092")
                        .database("discuz")
                        .server("jforum")
                        .account("informix","infobus")
                        .executor()
                ) // 导出结果集的供应商|根据配置决定用哪个?
                .strategy(new SchemaTablePrefixStrategy("qjob")) // 导出表的筛选策略|不设置相当于导出所有
                .target(new ConsoleSchemaOutput()) // 导出目标|模板咱选择?basic?full?
                .template(ExportTemplate.full()) // 自定义模板的实现
                .database("discuz", "jforum") // 导出的数据库名称
                .export(info); // 执行导出
    }
    // PostgreSQL 10.x
    private static void postgresqlExport(Information info){
        try {
            ExportExecutor
                    .supply(ResultSupplier
                            .postgresql()
                            .url("jdbc:postgresql://localhost:5432")
                            .database("postgres")
                            .account("postgres","root")
                            .executor()
                    ) // 导出结果集的供应商|根据配置决定用哪个?
                    .strategy(new SchemaTablePrefixStrategy("qrtz")) // 导出表的筛选策略|不设置相当于导出所有
                    .target(new ConsoleSchemaOutput()) // 导出目标|模板咱选择?basic?full?
                    .template(ExportTemplate.full()) // 自定义模板的实现
                    .database("postgres", "public") // 导出的数据库名称
                    .export(info); // 执行导出
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    // SQLServer 2019
    private static void mssqlExport(Information info){
        // SQLServer的SSL:://learn.microsoft.com/zh-cn/sql/connect/jdbc/connecting-with-ssl-encryption?view=sql-server-ver16
        try {
            ExportExecutor
                    .supply(ResultSupplier
                            .mssql()
                            .url("jdbc:sqlserver://192.168.1.140:1433;integratedSecurity=false;encrypt=false;trustServerCertificate=false;")
                            .database("ESB_pub_client")
                            .account("samer","infobus5144")
                            .executor()
                    ) // 导出结果集的供应商|根据配置决定用哪个?
                    .strategy(SchemaTableContainsStrategy.contact("JB", "FJH_SFJL", "FJH_ZXDA")) // 导出表的筛选策略|不设置相当于导出所有
                    .target(new FreeMarkerWordOutput(FILE_OUTPUT_DIR)) // 导出目标|模板咱选择?basic?full?
                    .template(ExportTemplate.full()) // 自定义模板的实现
                    .database("ESB_pub_client") // 导出的数据库名称
                    .export(info); // 执行导出
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    // Oracle 11G
    private static void oracleExport(Information info){
        // https://docs.oracle.com/database/121/JJDBC/urls.htm#JJDBC28275
        // jdbc:oracle:thin:@//localhost:5221/orcl
        // jdbc:oracle:driver_type:[username/password]@database_specifier
        try {
            ExportExecutor
                    .supply(ResultSupplier
                            .oracle()
                            .url("jdbc:oracle:thin:@//192.168.1.211:1521/orcl") // jdbc:oracle:thin:@localhost:5221:orcl
                            .schema("DMS")
                            .account("CHS","Chs123!@#")
                            .executor()
                    ) // 导出结果集的供应商|根据配置决定用哪个?
                    .strategy(SchemaTableContainsStrategy.of("PDS_JMS", "PDS_SUPPLY")) // 导出表的筛选策略|不设置相当于导出所有|new SchemaTablePrefixStrategy("PUB")
                    .target(new ConsoleSchemaOutput()) // 导出目标|模板咱选择?basic?full?
                    .template(ExportTemplate.full()) // 自定义模板的实现
                    .schema("DMS") // 导出的数据库名称
                    .export(info); // 执行导出
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    //RxJava2
    private static void rxMySQL(Information info){
        // 环境中有DataSource
        // Observable
        try {
            ExportReactiveExecutor
                    .supply(ResultRxJavaSupplier
                            .mysql()
                            .url(DB_URL)
                            .database("ftpro")
                            .account(DB_USERNAME, DB_PASSWORD)
                            .executor()
                    ) // 导出结果集的供应商|根据配置决定用哪个?
                    .strategy(new SchemaTablePrefixStrategy("think")) // (new SchemaTableRegMatchStrategy("'^(qrtz|spring)_'")) // 导出表的筛选策略|不设置相当于导出所有|new SchemaTablePrefixStrategy("apo")
                    .target(new DocxWordReactiveOutput(FILE_OUTPUT_DIR)) // 导出目标|模板咱选择?basic?full?
                    .template(ExportTemplate.full()) // 自定义模板的实现
                    .database("ftpro") // 导出的数据库名称
                    .export(info); // 执行导出
        }catch (Exception e){
            e.printStackTrace();
        }
        /*Flowable
        ExportReactiveExecutor
                .supply(new RxJavaMySQLExecutor(rxMySQLDB())) // 导出结果集的供应商|根据配置决定用哪个?
                .strategy(new SchemaTablePrefixStrategy("think")) // (new SchemaTableRegMatchStrategy("'^(qrtz|spring)_'")) // 导出表的筛选策略|不设置相当于导出所有|new SchemaTablePrefixStrategy("apo")
                .template(ExportTemplate.full()) // 自定义模板的实现
                .database("ftpro") // 导出的数据库名称
                .export(info, new ConsoleSchemaFlowableOutput()); // 执行导出*/


    }
    private static void postgreSQL(Information info){
        // 环境中有DataSource
        try {
             ExportReactiveExecutor
                    .supply(new RxJavaPostgreSQLExecutor(postgreSQLDB(), SchemaDialect.PostgreSQL)) // 导出结果集的供应商|根据配置决定用哪个?
                    .strategy(new SchemaTableRegMatchStrategy("'^(qrtz|spring)_'")) // 导出表的筛选策略|不设置相当于导出所有|new SchemaTablePrefixStrategy("apo")
                    .target(new ConsoleSchemaObservableOutput()) // 导出目标|模板咱选择?basic?full?
                    .template(ExportTemplate.full()) // 自定义模板的实现
                    .database("postgres", "public") // 导出的数据库名称
                    .export(info);// 执行导出
            // 何时结束？
            // disposable.dispose();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    private static void querySchema(){
        String sql = "SELECT t.tablename, obj_description(c.oid) AS description, c.oid FROM pg_tables t JOIN pg_class c ON t.tablename=c.relname WHERE t.schemaname=:schema";
        Disposable subscribe = postgreSQLDB().select(sql)
                .parameter("schema", "public")
                .get(rs -> Table.noSchema(0, "postgreSQL",
                        rs.getString("tableName"),
                        rs.getString("description")))
                .doOnNext(table -> System.out.println(table))
                .subscribe();
        if(subscribe.isDisposed()){
            System.out.println("CO<");
        }else{
            System.out.println("COM");
        }
    }


    private static Database postgreSQLDB(){
        return Database
                .nonBlocking()
                // the jdbc url of the connections to be placed in the pool
                .url("jdbc:postgresql://localhost:5432/postgres?currentSchema=public")
                .user("postgres")
                .password("root")
                // an unused connection will be closed after thirty minutes
                .maxIdleTime(30, TimeUnit.MINUTES)
                // connections are checked for healthiness on checkout if the connection
                // has been idle for at least 5 seconds
                .healthCheck(DatabaseType.POSTGRES)
                .idleTimeBeforeHealthCheck(5, TimeUnit.SECONDS)
                // if a connection fails creation then retry after 30 seconds
                .connectionRetryInterval(30, TimeUnit.SECONDS)
                // the maximum number of connections in the pool
                .maxPoolSize(3)
                .build();
    }
    private static Database rxMySQLDB(){
        return Database
                .nonBlocking()
                // the jdbc url of the connections to be placed in the pool
                //.url("jdbc:mysql://127.0.0.1:3306?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&useSSL=false&zeroDateTimeBehavior=convertToNull&useInformationSchema=true")
                .url(DB_URL)
                .user(DB_USERNAME)
                .password(DB_PASSWORD)
                // an unused connection will be closed after thirty minutes
                .maxIdleTime(30, TimeUnit.MINUTES)
                // connections are checked for healthiness on checkout if the connection
                // has been idle for at least 5 seconds
                .healthCheck(DatabaseType.MYSQL)
                .idleTimeBeforeHealthCheck(5, TimeUnit.SECONDS)
                // if a connection fails creation then retry after 30 seconds
                .connectionRetryInterval(30, TimeUnit.SECONDS)
                // the maximum number of connections in the pool
                .maxPoolSize(3)
                .property("useInformationSchema", true)
                .build();
    }

    private static DataSource configHikariDataSource(){
        // 数据源  创建HikariConfig配置类
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setDriverClassName(Driver_Class_Name);
        hikariConfig.setJdbcUrl(DB_URL); //("jdbc:mysql://127.0.0.1:3306?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&useSSL=false&zeroDateTimeBehavior=convertToNull");
        // hikariConfig.setJdbcUrl(DB_URL);
        hikariConfig.setUsername(DB_USERNAME);
        hikariConfig.setPassword(DB_PASSWORD);
        // 设置useInformationSchema 可以获取tables表注释信息 即解决数据库表和列字段有说明、生成文档没有说明
        hikariConfig.addDataSourceProperty("useInformationSchema", "true");
        hikariConfig.setMinimumIdle(2);
        hikariConfig.setMaximumPoolSize(5);
        return new HikariDataSource(hikariConfig);
    }

}

